package cron

import (
	"cloudops/src/common"
	"cloudops/src/models"
	"context"
	"encoding/json"
	"fmt"
	"github.com/gammazero/workerpool"
	"go.uber.org/zap"
	"k8s.io/apimachinery/pkg/util/wait"
	"strings"
	"time"
)

// NotifyOrderManager 工单通知管理
func (cm *CronManager) NotifyOrderManager(ctx context.Context) error {
	go wait.UntilWithContext(ctx, cm.RunNotifyOrder, time.Duration(cm.Sc.WorkOrderAutoActionC.NotifyRunIntervalSeconds)*time.Second)
	<-ctx.Done()
	cm.Sc.Logger.Info("RunNotifyOrder 收到其他任务退出信号 退出")
	return nil
}

// RunNotifyOrder 获取待通知工单，执行通知逻辑
func (cm *CronManager) RunNotifyOrder(ctx context.Context) {
	start := time.Now()
	pendingOrders, count, err := models.GetWorkOrderInstanceUnfinished([]string{common.WorkOrderInstanceStatusFinished, common.WorkOrderInstanceStatusApprovalReject})
	if err != nil {
		cm.Sc.Logger.Error("工单通知模块 获取工单失败", zap.Error(err))
		return
	}
	cm.Sc.Logger.Info("工单通知模块 获取到待通知工单", zap.Int64("count", count))

	wp := workerpool.New(cm.Sc.WorkOrderAutoActionC.BatchNum)
	for _, pendingOrder := range pendingOrders {
		pendingOrder := pendingOrder
		wp.Submit(func() {
			// 启动任务
			cm.RunNotifyOrderOne(pendingOrder)
		})
	}
	// 等待所有任务执行完成
	wp.StopWait()
	tookSeconds := time.Since(start).Seconds()
	cm.Sc.Logger.Info("工单自动执行 执行完成耗时", zap.Float64("seconds", tookSeconds), zap.Int64("总数", count))
}

// RunNotifyOrderOne 执行通知工单逻辑
func (cm *CronManager) RunNotifyOrderOne(pendingOrder *models.WorkOrderInstance) {
	cm.Sc.Logger.Info("工单通知模块 开始执行通知逻辑", zap.Any("name", pendingOrder.Name))

	// 钉钉的消息填充
	pendingOrder.FillFrontAllDataWorkOrderInstance()
	template := pendingOrder.Template
	if template == nil {
		return
	}
	// 这里的共性是找到下一个节点 遍历通知他们需要动作
	// 处理时间轴
	var actualFlowData []models.FlowNode
	err := json.Unmarshal([]byte(pendingOrder.ActualFlowData), &actualFlowData)
	if err != nil {
		cm.Sc.Logger.Error("工单通知模块 解析时间轴数据json失败", zap.String("工单名称", pendingOrder.Name), zap.Error(err))
		return
	}

	// 找到下一个未执行的节点
	tarGetNode := models.FlowNode{}
	for _, node := range actualFlowData {
		node := node
		if node.ActualUser == "" {
			tarGetNode = node
			break
		}
	}

	// 获取需要通知的人或组
	targetUsers := []*models.User{}
	splits := strings.Split(tarGetNode.DefineUserOrGroup, "@")

	switch splits[0] {
	case "用户":
		userName := splits[1]
		dbUser, err := models.GetUserByUserName(userName)
		if err != nil {
			cm.Sc.Logger.Error("工单通知模块 获取用户失败", zap.String("用户名", dbUser.RealName), zap.Error(err))
			return
		}
		targetUsers = append(targetUsers, dbUser)
	case "组":
		roleValue := splits[1]
		role, err := models.GetRoleByRoleValue(roleValue)
		if err != nil {
			cm.Sc.Logger.Error("工单通知模块 获取用户组失败", zap.Any("角色值", roleValue), zap.Error(err))
			return
		}
		targetUsers = append(targetUsers, role.Users...)
	}

	detailUrl := fmt.Sprintf("%s%s%d",
		cm.Sc.Domain,
		cm.Sc.WorkOrderAutoActionC.DetailUrl,
		pendingOrder.ID,
	)
	// 拼接通知消息
	notifyMsg := fmt.Sprintf(
		"尊敬的用户：@%v，您有一个%v工单。\n"+
			"工单名称：%v\n"+
			"创建人：@%v\n"+
			"期望完成时间：%v\n"+
			"查看详情请点击链接：%v",
		pendingOrder.CreateUserName,                             // 在用户名前加 @ 符号
		common.WorkOrderInstanceStatus_MAP[pendingOrder.Status], // 工单状态
		pendingOrder.Name,                                       // 工单名称
		pendingOrder.CreateUserName,                             // 在创建人名前加 @ 符号
		pendingOrder.DesireFinishTime,                           // 期望完成时间
		detailUrl,                                               // 工单详情链接
	)

	// 遍历通知
	for _, user := range targetUsers {
		user := user
		imc := cm.Sc.ImDingDingC
		imc.AtMobiles = []string{user.Mobile}
		imc.Title = fmt.Sprintf("工单提醒:@%v", user.RealName)
		common.DingDingMsgDirectSend(imc, notifyMsg)
	}
}
